<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>JavaScript 获取输入时的光标位置及场景问题 · ShaofeiZi Blog · 做个日常记录</title>
    <meta name="description" content="JavaScript 如何获取输入时的光标位置，在光标处插入或替换内容">
    <link rel="shortcut icon" href="/BLOG/favicon.ico">
  <link rel="manifest" href="/BLOG/manifest.json">
  <meta name="theme-color" content="#3F51B5">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <link rel="apple-touch-icon" href="/BLOG/icons/192.png">
  <link rel="mask-icon" href="/BLOG/icons/safari-pinned-tab.svg" color="#3eaf7c">
  <meta name="msapplication-TileImage" content="/icons/192.png">
  <meta name="msapplication-TileColor" content="#3F51B5">
    
    <link rel="preload" href="/BLOG/assets/css/42.styles.90045bd1.css" as="style"><link rel="preload" href="/BLOG/assets/js/app.1a725be8.js" as="script"><link rel="preload" href="/BLOG/assets/js/35.825f3d75.js" as="script"><link rel="prefetch" href="/BLOG/assets/js/7.88ba0bb7.js"><link rel="prefetch" href="/BLOG/assets/js/0.d3e592bd.js"><link rel="prefetch" href="/BLOG/assets/js/1.39b9c99c.js"><link rel="prefetch" href="/BLOG/assets/js/2.68dc10c9.js"><link rel="prefetch" href="/BLOG/assets/js/3.dfebdd5e.js"><link rel="prefetch" href="/BLOG/assets/js/4.ea97a821.js"><link rel="prefetch" href="/BLOG/assets/js/5.d8c2ecbf.js"><link rel="prefetch" href="/BLOG/assets/js/6.e51cd79c.js"><link rel="prefetch" href="/BLOG/assets/js/8.d9eebc06.js"><link rel="prefetch" href="/BLOG/assets/js/9.1a541d13.js"><link rel="prefetch" href="/BLOG/assets/js/10.4ec9ca67.js"><link rel="prefetch" href="/BLOG/assets/js/11.02558377.js"><link rel="prefetch" href="/BLOG/assets/js/12.d0e2086f.js"><link rel="prefetch" href="/BLOG/assets/js/13.5af02ddd.js"><link rel="prefetch" href="/BLOG/assets/js/14.5d9fcbf2.js"><link rel="prefetch" href="/BLOG/assets/js/15.ca0178b2.js"><link rel="prefetch" href="/BLOG/assets/js/16.cd99d056.js"><link rel="prefetch" href="/BLOG/assets/js/17.56f11c1d.js"><link rel="prefetch" href="/BLOG/assets/js/18.21837cc7.js"><link rel="prefetch" href="/BLOG/assets/js/19.73335fea.js"><link rel="prefetch" href="/BLOG/assets/js/20.1632ab79.js"><link rel="prefetch" href="/BLOG/assets/js/21.43175244.js"><link rel="prefetch" href="/BLOG/assets/js/22.5b7c0cca.js"><link rel="prefetch" href="/BLOG/assets/js/23.e624ba97.js"><link rel="prefetch" href="/BLOG/assets/js/24.ac5f7b41.js"><link rel="prefetch" href="/BLOG/assets/js/25.6934a11d.js"><link rel="prefetch" href="/BLOG/assets/js/26.407b2583.js"><link rel="prefetch" href="/BLOG/assets/js/27.7449d673.js"><link rel="prefetch" href="/BLOG/assets/js/28.52e25437.js"><link rel="prefetch" href="/BLOG/assets/js/29.fba21f3a.js"><link rel="prefetch" href="/BLOG/assets/js/30.2cd6d3e2.js"><link rel="prefetch" href="/BLOG/assets/js/31.0b0a749f.js"><link rel="prefetch" href="/BLOG/assets/js/32.92134487.js"><link rel="prefetch" href="/BLOG/assets/js/33.ad2b89cc.js"><link rel="prefetch" href="/BLOG/assets/js/34.9b22334e.js"><link rel="prefetch" href="/BLOG/assets/js/36.cc3da84c.js"><link rel="prefetch" href="/BLOG/assets/js/37.8f339f62.js"><link rel="prefetch" href="/BLOG/assets/js/38.5674618f.js"><link rel="prefetch" href="/BLOG/assets/js/39.180f0d85.js"><link rel="prefetch" href="/BLOG/assets/js/40.275f26e3.js"><link rel="prefetch" href="/BLOG/assets/js/41.ce0f5927.js">
    <link rel="stylesheet" href="/BLOG/assets/css/42.styles.90045bd1.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div data-app="true" id="app" class="application theme--light"><div class="application--wrap"><div class="v-progress-linear blog-progress" style="height:3px;display:none;"><div class="v-progress-linear__background accent" style="height:3px;opacity:0.4;width:100%;"></div><div class="v-progress-linear__bar"><!----><div class="v-progress-linear__bar__determinate accent" style="width:0%;"></div></div></div><aside class="v-navigation-drawer v-navigation-drawer--close v-navigation-drawer--fixed v-navigation-drawer--is-mobile" style="height:100%;margin-top:0px;max-height:calc(100% - 0px);transform:translateX(-240px);width:240px;"><div><div class="aside-brand-wrap"><div class="aside-brand"><a href="/BLOG/" class="aside-avatar elevation-2 router-link-active"><img src="/BLOG/face.png" alt="avatar"></a><hgroup class="mt-3 variant-hide"><div class="subheading white--text">訾绍飞</div><a href="mailto:zishaofei221@gmail.com" title="zishaofei221@gmail.com" class="aside-mail primary--text text--lighten-5">zishaofei221@gmail.com</a></hgroup></div></div><hr class="v-divider theme--dark"><div class="v-list nav-list"><div class="secondary--text"><a href="/BLOG/" class="v-list__tile v-list__tile--link"><div class="v-list__tile__avatar"><div class="v-avatar" style="height:40px;width:40px;"><i class="fa fa-home"></i></div></div><div class="v-list__tile__content">首页</div></a></div><div class="secondary--text"><a href="/BLOG/tags" class="v-list__tile v-list__tile--link"><div class="v-list__tile__avatar"><div class="v-avatar" style="height:40px;width:40px;"><i class="fa fa-tag"></i></div></div><div class="v-list__tile__content">标签</div></a></div><div class="secondary--text"><a href="https://github.com/ShaofeiZi" target="_blank" class="v-list__tile v-list__tile--link"><div class="v-list__tile__avatar"><div class="v-avatar" style="height:40px;width:40px;"><i class="fab fa-github"></i></div></div><div class="v-list__tile__content">Github</div></a></div><div class="secondary--text"><a href="/BLOG/about" class="v-list__tile v-list__tile--link"><div class="v-list__tile__avatar"><div class="v-avatar" style="height:40px;width:40px;"><i class="fa fa-user-secret"></i></div></div><div class="v-list__tile__content">About</div></a></div></div></div><div class="v-navigation-drawer__border"></div></aside><nav class="blog-toolbar v-toolbar v-toolbar--fixed theme--dark primary" style="margin-top:0px;padding-right:0px;padding-left:0px;transform:translateY(0px);"><div class="v-toolbar__content" style="height:56px;"><button type="button" class="v-btn v-btn--icon"><div class="v-btn__content"><i class="fa fa-bars"></i></div></button><div class="v-toolbar__title">JavaScript 获取输入时的光标位置及场景问题</div><div class="spacer"></div><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""><!----></div><div class="v-menu" style="display:none;"><div class="v-menu__activator"><button type="button" class="v-btn v-btn--icon"><div class="v-btn__content"><i class="fa fa-share-alt"></i></div></button></div><div class="v-menu__content" style="max-height:auto;min-width:0px;max-width:auto;top:12px;left:0px;transform-origin:top right;z-index:0;display:none;"><div class="v-list"><div class="secondary--text"><a class="v-list__tile v-list__tile--link"><div class="v-list__tile__avatar"><div class="v-avatar" style="height:40px;width:40px;"><i class="fa fa-lg fa-copy"></i></div></div><div class="v-list__tile__title">复制链接</div></a></div></div><input type="text" tabindex="-1" aria-hidden="true" value="" class="fake-hide"></div></div></div></nav><main class="v-content" style="padding-top:56px;padding-right:0px;padding-bottom:0px;padding-left:0px;"><div class="v-content__wrap"><div class="container blog-container grid-list-xl align-center"><div class="layout row wrap"><div class="flex mb-3 xs12"><article class="v-card elevation-16 post-card" style="height:undefined;"><div class="v-card__title"><div class="flex xs12"><h2 class="display-1 mb-3">JavaScript 获取输入时的光标位置及场景问题</h2><div class="post-meta"><time datetime="2016-11-25T10:02:01.000Z" class="secondary--text post-time">2016年11月25日</time></div></div></div><div class="v-card__text pt-0 pb-0"><div class="flex xs12"><div class="content custom"><h2 id="前言"><a href="#前言" aria-hidden="true" class="header-anchor">#</a> 前言</h2><p>在输入编辑的业务场景中，可能会需要在光标当前的位置或附近显示提示选项。比如社交评论中的<code>@user</code>功能，要确保提示的用户列表总是出现在<code>@</code>字符右下方，又或者是在自定义编辑器中 autocomplete 语法提示，都需要获取光标当前的位置作为参照点。</p><h2 id="两种位置"><a href="#两种位置" aria-hidden="true" class="header-anchor">#</a> 两种位置</h2><p>对于 WEB 开发来讲，当我们提到某某元素的位置，通常是指这个元素相对于父级或文档的像素单位坐标。而对于输入框中光标，就有了额外的区分。</p><h3 id="相对于内容"><a href="#相对于内容" aria-hidden="true" class="header-anchor">#</a> 相对于内容</h3><p>相对于内容，光标位于第几个字符之后，姑且称之为<strong>字符位置</strong>吧。</p><h3 id="相对于-ui"><a href="#相对于-ui" aria-hidden="true" class="header-anchor">#</a> 相对于 UI</h3><p>相对于 UI，也就是跟普通页面元素一样的<strong>像素位置</strong>了。</p><h2 id="插入或替换内容"><a href="#插入或替换内容" aria-hidden="true" class="header-anchor">#</a> 插入或替换内容</h2><p>在前言提到的场景中，也有在光标位置处插入内容的需求，比如对选取文字加粗<code>text =&gt; &lt;strong&gt;text&lt;/strong&gt;</code>等。</p><h3 id="textarea"><a href="#textarea" aria-hidden="true" class="header-anchor">#</a> textarea</h3><p><code>textarea</code>元素可以很容易获取到选择的一段文字的起止位置。如果当前没有选择文字，则两个位置值都为光标右侧字符的索引，从 0 开始。</p><pre class="language-js"><code><span class="token comment">// 开始位置</span>
textarea<span class="token punctuation">.</span>selectionStart
<span class="token comment">// 结束位置</span>
textarea<span class="token punctuation">.</span>selectionEnd
</code></pre><p>对于加粗功能，有了起止位置，就能获取到选择的文字内容，然后对内容进行替换。由于<code>textarea</code>不能包含子元素，只有纯文本，所以基于<code>textarea</code>实现加粗只能像用 Markdown 标记语法实现。</p><pre class="language-js"><code><span class="token keyword">var</span> selectedText <span class="token operator">=</span> textarea<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>textarea<span class="token punctuation">.</span>selectionStart<span class="token punctuation">,</span> textarea<span class="token punctuation">.</span>selectionEnd<span class="token punctuation">)</span>
textarea<span class="token punctuation">.</span><span class="token function">setRangeText</span><span class="token punctuation">(</span><span class="token string">'**'</span> <span class="token operator">+</span> selectedText <span class="token operator">+</span> <span class="token string">'**'</span><span class="token punctuation">)</span>
</code></pre><blockquote><p><code>textarea.setRangeText(text: String)</code> 把选中的文字替换为其他内容。</p></blockquote><h3 id="contenteditable"><a href="#contenteditable" aria-hidden="true" class="header-anchor">#</a> contenteditable</h3><p>也可能我们会使用<code>contenteditable</code>属性把一个元素变为可编辑元素。而上面所用的属性和函数都是普通元素所没有的，所以要换一种姿势实现。</p><p>还是以加粗功能为例。</p><pre class="language-js"><code><span class="token comment">// 获取文档中选中区域</span>
<span class="token keyword">var</span> range <span class="token operator">=</span> window<span class="token punctuation">.</span><span class="token function">getSelection</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getRangeAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
<span class="token keyword">var</span> strongNode <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'strong'</span><span class="token punctuation">)</span>
<span class="token comment">// 选中区域文本</span>
strongNode<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> range<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 删除选中区</span>
range<span class="token punctuation">.</span><span class="token function">deleteContents</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 在光标处插入新节点</span>
range<span class="token punctuation">.</span><span class="token function">insertNode</span><span class="token punctuation">(</span>strongNode<span class="token punctuation">)</span>
</code></pre><p>基于<code>contenteditable</code>的可编辑元素，其中的内容均为子元素，文本为<code>textNode</code>，加粗使用 HTML 元素，插入或替换是对元素的操作。</p><p>如果想使用操作内容的思路实现会比较麻烦，因为可以获取到的起止位置是基于子元素的。</p><pre class="language-html"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">contenteditable</span><span class="token punctuation">&gt;</span></span>hello<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>你好<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>big</span><span class="token punctuation">&gt;</span></span>w<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>big</span><span class="token punctuation">&gt;</span></span>orld<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>假如选中的文字是<code>你好wor</code>，调用相关 API 的输出如下。</p><pre class="language-js"><code><span class="token comment">// 当前在文档中选择的文本，document 和 window 都有这个函数</span>
<span class="token comment">// var selection = document.getSelection()</span>
<span class="token keyword">var</span> selection <span class="token operator">=</span> window<span class="token punctuation">.</span><span class="token function">getSelection</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
selection<span class="token punctuation">.</span>anchorNode <span class="token comment">// 你好</span>
selection<span class="token punctuation">.</span>anchorOffset <span class="token comment">// 0</span>
selection<span class="token punctuation">.</span>focusNode <span class="token comment">// orld</span>
selection<span class="token punctuation">.</span>focusOffset <span class="token comment">// 2</span>

<span class="token comment">// 或者使用 Range</span>
<span class="token keyword">var</span> range <span class="token operator">=</span> selection<span class="token punctuation">.</span><span class="token function">getRangeAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
range<span class="token punctuation">.</span>startContainer <span class="token comment">// 你好</span>
range<span class="token punctuation">.</span>startOffset <span class="token comment">// 0</span>
range<span class="token punctuation">.</span>endContainer <span class="token comment">// orld</span>
range<span class="token punctuation">.</span>endOffset <span class="token comment">// 2</span>
</code></pre><p>最终可以获取到起止元素以及选中区域在开始元素内容中的字符位置和在结束元素内容中的字符位置。其中的起止元素均为<code>textNode</code>类型，通过<code>parentNode</code>获取到包裹元素。</p><pre class="language-js"><code>range<span class="token punctuation">.</span>startContainer<span class="token punctuation">.</span>parentNode <span class="token comment">// &lt;strong&gt;你好&lt;/strong&gt;</span>
range<span class="token punctuation">.</span>endContainer<span class="token punctuation">.</span>parentNode <span class="token comment">// &lt;div contenteditable&gt;...&lt;/div&gt;</span>
</code></pre><blockquote><p>需要注意的是通过<code>Selection</code>和<code>Rang</code>获取到起止位置是有方向之分的，从左向右选择和从右向左选择得到的值是正好相反的。</p></blockquote><h2 id="基于光标像素位置创建内容"><a href="#基于光标像素位置创建内容" aria-hidden="true" class="header-anchor">#</a> 基于光标像素位置创建内容</h2><p>这里就要开始用像素位置，同样分为两种实现来讲。</p><h3 id="contenteditable-2"><a href="#contenteditable-2" aria-hidden="true" class="header-anchor">#</a> contenteditable</h3><p>可编辑元素获取光标像素位置就像<code>textarea</code>获取光标的字符位置一样简单。</p><pre class="language-js"><code><span class="token keyword">var</span> range <span class="token operator">=</span> window<span class="token punctuation">.</span><span class="token function">getSelection</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getRangeAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span>
range<span class="token punctuation">.</span><span class="token function">getBoundingClientRect</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// { width, height, top, right, bottom, right }</span>
</code></pre><p>这么具体的尺寸值，实现自动完成真是 So easy!</p><h3 id="textarea-2"><a href="#textarea-2" aria-hidden="true" class="header-anchor">#</a> textarea</h3><p><code>textarea</code>其中的内容都是纯文本，在 DOM 中不存在相关的对象，对于像素位置就得另作他想了。</p><h4 id="基于行高和字体大小计算"><a href="#基于行高和字体大小计算" aria-hidden="true" class="header-anchor">#</a> 基于行高和字体大小计算</h4><pre class="language-js"><code><span class="token comment">// 1.获取光标结束位置</span>
<span class="token keyword">var</span> end <span class="token operator">=</span> textarea<span class="token punctuation">.</span>selectionEnd
<span class="token comment">// 2.通过匹配光标之前文本中的换行符计算所在行</span>
<span class="token keyword">var</span> row <span class="token operator">=</span> textarea<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> end<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex">/\r\n|\r|\n/</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length
<span class="token comment">// 3.计算 top，行高 * 行数 + 上填充 + 边框宽度</span>
<span class="token keyword">var</span> top <span class="token operator">=</span> lineHeight <span class="token operator">*</span> <span class="token punctuation">(</span>row <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> paddingTop <span class="token operator">+</span> borderWidth
<span class="token comment">// 4.获取光标左侧的文本</span>
<span class="token keyword">var</span> leftText <span class="token operator">=</span> textarea<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token regex">/\r\n|\r|\n/</span><span class="token punctuation">)</span><span class="token punctuation">[</span>row<span class="token punctuation">]</span>
<span class="token comment">// 5.影响一段文字所占宽度的因素太多，除字体大小、中英文、符号、字符间距等，还有字体、浏览器、系统等客观因素</span>
<span class="token comment">// var left = ...</span>
</code></pre><p>这个方案的思路是没问题的，但是考虑所有问题的成本太高。虽然可以创建测试元素去计算文本宽度，但这个方案本身是从严谨的角度出发的。与其混在一块，直接用取巧的办法更简单。</p><blockquote><p><s>这个方案的潜台词是：明明可以靠脸吃饭，却偏偏要靠才华！</s> 🙄</p></blockquote><h4 id="镜像元素"><a href="#镜像元素" aria-hidden="true" class="header-anchor">#</a> 镜像元素</h4><p>文本不支持定位？那我创建 DOM 好了。</p><pre class="language-js"><code><span class="token comment">// 光标位置</span>
<span class="token keyword">var</span> end <span class="token operator">=</span> textarea<span class="token punctuation">.</span>selectionEnd
<span class="token comment">// 光标前的内容</span>
<span class="token keyword">var</span> beforeText <span class="token operator">=</span> textarea<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> end<span class="token punctuation">)</span>
<span class="token comment">// 光标后的内容</span>
<span class="token keyword">var</span> afterText <span class="token operator">=</span> textarea<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span>end<span class="token punctuation">)</span>
<span class="token comment">// 对影响 UI 的特殊元素编码</span>
<span class="token keyword">var</span> <span class="token function-variable function">escape</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> text<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex">/&lt;|&gt;|`|&quot;|&amp;/g</span><span class="token punctuation">,</span> <span class="token string">'?'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex">/\r\n|\r|\n/g</span><span class="token punctuation">,</span> <span class="token string">'&lt;br&gt;'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token comment">// 创建镜像内容，复制样式</span>
<span class="token keyword">var</span> mirror <span class="token operator">=</span> <span class="token string">'&lt;div class=&quot;'</span> <span class="token operator">+</span> textarea<span class="token punctuation">.</span>className <span class="token operator">+</span> <span class="token string">'&quot;&gt;'</span> <span class="token operator">+</span> <span class="token function">escape</span><span class="token punctuation">(</span>beforeText<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'&lt;span id=&quot;cursor&quot;&gt;|&lt;/span&gt;'</span> <span class="token operator">+</span> <span class="token function">escape</span><span class="token punctuation">(</span>afterText<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'&lt;/div&gt;'</span>
<span class="token comment">// 添加到 textarea 同级，注意设置定位及 zIndex，使两个元素重合</span>
textarea<span class="token punctuation">.</span><span class="token function">insertAdjacentHTML</span><span class="token punctuation">(</span><span class="token string">'afterend'</span><span class="token punctuation">,</span> mirror<span class="token punctuation">)</span>
<span class="token comment">// 通过镜像元素中的假光标占位元素获取像素位置</span>
<span class="token keyword">var</span> cursor <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'cursor'</span><span class="token punctuation">)</span>
cursor<span class="token punctuation">.</span><span class="token function">getBoundingClientRect</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// { width, height, top, right, bottom, right }</span>
</code></pre><h2 id="end"><a href="#end" aria-hidden="true" class="header-anchor">#</a> End</h2><p>最后悄悄说一句，以上内容不兼容低版本 IE，但是 IE 毕竟主场运行，有些 API 反而是其他浏览器所没有的。就上面提到的案例来说，低版本 IE 也有对应的 API 可用。真是不想在 IE 上去浪费精力了，索性不提。</p></div></div></div><div class="v-card__actions"><div class="flex xs12"><a href="/BLOG/tags/JavaScript"><span tabindex="0" class="v-chip capitalize chip-tag v-chip--label v-chip--small"><span class="v-chip__content">JavaScript</span></span></a><a href="/BLOG/tags/Range"><span tabindex="0" class="v-chip capitalize chip-tag v-chip--label v-chip--small"><span class="v-chip__content">Range</span></span></a><a href="/BLOG/tags/Selection"><span tabindex="0" class="v-chip capitalize chip-tag v-chip--label v-chip--small"><span class="v-chip__content">Selection</span></span></a></div></div></article></div><div class="flex text-xs-left xs6"><a href="/BLOG/posts/webpack-use-lodash.html" class="post-nav v-btn v-btn--flat v-btn--router"><div class="v-btn__content"><div class="grey--text"><i class="fa mr-1 fa-chevron-left"></i>Prev</div><div class="title mt-1 primary--text hidden-xs-only">Webpack按需打包Lodash的几种方式</div></div></a></div><div class="flex text-xs-right xs6"><a href="/BLOG/posts/text-truncation.html" class="post-nav v-btn v-btn--flat v-btn--router"><div class="v-btn__content"><div class="grey--text">Next
          <i class="fa ml-1 fa-chevron-right"></i></div><div class="title mt-1 primary--text hidden-xs-only">前端文字的截断处理</div></div></a></div><div class="flex mt-3 xs12"><div class="v-card" style="height:undefined;"><div class="v-card__title"><span class="headline">Comment</span></div></div></div></div></div><footer class="v-footer blog-footer darken-1 mt-3 theme--dark" style="height:auto;"><div class="primary--text text--lighten-4 text-xs-center py-3 v-card v-card--flat v-card--tile primary" style="height:undefined;"><div class="v-card__text pb-0">博客内容遵循 <a rel="license noopener noreferrer" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" target="_blank">知识共享 署名 - 非商业性 - 相同方式共享 4.0 国际协议</a></div><div class="v-card__text pt-0 mt-1"><span>訾绍飞 © 2015 - 2018</span><span><!---->
        Power by
        <a href="https://vuepress.vuejs.org" target="_blank" rel="noopener noreferrer">VuePress</a> Theme
        <a href="https://github.com/ShaofeiZi/BLOG" target="_blank" rel="noopener noreferrer">indigo</a></span></div></div></footer></div></main><button type="button" class="v-btn v-btn--bottom v-btn--floating v-btn--fixed v-btn--right accent" style="display:none;"><div class="v-btn__content"><i class="fa fa-lg fa-chevron-up"></i></div></button></div></div></div>
    <script src="/BLOG/assets/js/35.825f3d75.js" defer></script><script src="/BLOG/assets/js/app.1a725be8.js" defer></script>
  </body>
</html>
